package com.kexio.file.controller;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.kexio.auth.annotation.Audit;
import com.kexio.auth.annotation.PublicApi;
import com.kexio.auth.annotation.RateLimit;
import com.kexio.auth.annotation.RequiresPermission;
import com.kexio.common.dto.PageResponse;
import com.kexio.common.dto.Result;
import com.kexio.file.entity.FileInfo;
import com.kexio.file.service.FileInfoService;
import com.kexio.file.vo.FileInfoVO;

import cn.dev33.satoken.annotation.SaIgnore;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

/**
 * 文件信息管理控制器
 * 
 * 提供文件管理相关的REST API，包括：
 * - 文件的上传和下载
 * - 文件的增删改查
 * - 文件分页查询
 * - 文件统计信息
 * - 文件维护操作
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Tag(name = "文件信息管理", description = "文件信息管理相关接口")
@RestController
@RequestMapping("/file/fileinfo")
public class FileInfoController {
    
    private static final Logger log = LoggerFactory.getLogger(FileInfoController.class);
    
    @Autowired
    private FileInfoService fileInfoService;
    
    // ==================== 文件上传 ====================
    
    @Operation(summary = "单文件上传", description = "上传单个文件")
    @PostMapping("/upload")
    @RequiresPermission("file:fileinfo:upload")
    @RateLimit(key = "file:upload", count = 10, time = 60, limitType = RateLimit.LimitType.USER)
    @Audit(value = "文件上传")
    public Result<FileInfoVO> uploadFile(
            @Parameter(description = "上传的文件") @RequestParam("file") MultipartFile file,
            @Parameter(description = "业务类型") @RequestParam("businessType") String businessType,
            @Parameter(description = "业务键") @RequestParam(value = "businessKey", required = false) String businessKey,
            @Parameter(description = "文件描述") @RequestParam(value = "description", required = false) String description,
            @Parameter(description = "是否公开") @RequestParam(value = "isPublic", defaultValue = "false") Boolean isPublic,
            @Parameter(description = "过期时间") @RequestParam(value = "expireTime", required = false) LocalDateTime expireTime) {
        
        try {
            FileInfo fileInfo = fileInfoService.uploadFile(file, businessType, businessKey, description, isPublic, expireTime);
            FileInfoVO vo = fileInfoService.getVOById(fileInfo.getId());
            return Result.success(vo);
        } catch (Exception e) {
            log.error("文件上传失败", e);
            return Result.error("文件上传失败: " + e.getMessage());
        }
    }
    
    @Operation(summary = "批量文件上传", description = "上传多个文件")
    @PostMapping("/upload/batch")
    @RequiresPermission("file:fileinfo:upload")
    @RateLimit(key = "file:upload:batch", count = 5, time = 60, limitType = RateLimit.LimitType.USER)
    @Audit(value = "批量文件上传")
    public Result<List<FileInfoVO>> uploadFiles(
            @Parameter(description = "上传的文件列表") @RequestParam("files") List<MultipartFile> files,
            @Parameter(description = "业务类型") @RequestParam("businessType") String businessType,
            @Parameter(description = "业务键") @RequestParam(value = "businessKey", required = false) String businessKey,
            @Parameter(description = "是否公开") @RequestParam(value = "isPublic", defaultValue = "false") Boolean isPublic) {
        
        try {
            List<FileInfo> fileInfos = fileInfoService.uploadFiles(files, businessType, businessKey, isPublic);
            List<FileInfoVO> vos = fileInfos.stream()
                    .map(fileInfo -> fileInfoService.getVOById(fileInfo.getId()))
                    .collect(java.util.stream.Collectors.toList());
            return Result.success(vos);
        } catch (Exception e) {
            log.error("批量文件上传失败", e);
            return Result.error("批量文件上传失败: " + e.getMessage());
        }
    }
    
    // ==================== 文件下载和预览 ====================
    
    @Operation(summary = "文件下载", description = "下载文件")
    @GetMapping("/download/{fileId}")
    @RequiresPermission("file:fileinfo:download")
    @RateLimit(key = "file:download", count = 100, time = 60, limitType = RateLimit.LimitType.USER)
    @Audit(value = "文件下载")
    public ResponseEntity<InputStreamResource> downloadFile(
            @Parameter(description = "文件ID") @PathVariable String fileId) {
        
        try {
            FileInfo fileInfo = fileInfoService.getById(fileId);
            if (fileInfo == null) {
                return ResponseEntity.notFound().build();
            }
            
            java.io.InputStream inputStream = fileInfoService.downloadFile(fileId, true);
            if (inputStream == null) {
                return ResponseEntity.notFound().build();
            }
            
            HttpHeaders headers = new HttpHeaders();
            String encodedFileName = java.net.URLEncoder.encode(fileInfo.getDisplayName(), java.nio.charset.StandardCharsets.UTF_8)
                    .replaceAll("\\+", "%20");
            headers.add(HttpHeaders.CONTENT_DISPOSITION, 
                       "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
            headers.add(HttpHeaders.CONTENT_TYPE, fileInfo.getMimeType());
            
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentLength(fileInfo.getFileSize() != null ? fileInfo.getFileSize() : -1)
                    .body(new InputStreamResource(inputStream));
                    
        } catch (Exception e) {
            log.error("文件下载失败", e);
            return ResponseEntity.internalServerError().build();
        }
    }
    
    /**
     * 公开图片预览接口
     * 用于预览所有图片文件，无需权限验证
     */
    @Operation(summary = "公开图片预览", description = "预览所有图片文件，无需权限验证")
    @GetMapping("/preview/{fileId}")
    @SaIgnore
//    @PublicApi(description = "公开图片/视频预览接口")
    @RateLimit(key = "file:preview", count = 500, time = 60, limitType = RateLimit.LimitType.IP)
    public ResponseEntity<InputStreamResource> previewImage(
            @Parameter(description = "文件ID") @PathVariable String fileId) {
        
        try {
            FileInfo fileInfo = fileInfoService.getById(fileId);
            if (fileInfo == null) {
                log.warn("文件不存在: {}", fileId);
                return ResponseEntity.notFound().build();
            }
            
            // 🔧 修复：支持图片和视频预览
            String mimeType = fileInfo.getMimeType();
            if (mimeType == null || (!mimeType.startsWith("image/") && !mimeType.startsWith("video/"))) {
                log.warn("尝试预览不支持的文件类型: {}, mimeType: {}", fileId, mimeType);
                return ResponseEntity.notFound().build();
            }
            
            // 记录预览行为（用于安全审计）
            log.info("媒体预览请求: fileId={}, fileName={}, mimeType={}, businessType={}, isPublic={}", 
                fileId, fileInfo.getDisplayName(), mimeType, fileInfo.getBusinessType(), fileInfo.getIsPublic());
            
            java.io.InputStream inputStream = fileInfoService.downloadFile(fileId, false);
            if (inputStream == null) {
                log.warn("无法获取文件流: {}", fileId);
                return ResponseEntity.notFound().build();
            }
            
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_TYPE, mimeType);
            String encodedFileName = java.net.URLEncoder.encode(fileInfo.getDisplayName(), java.nio.charset.StandardCharsets.UTF_8)
                    .replaceAll("\\+", "%20");
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
            headers.add(HttpHeaders.CACHE_CONTROL, "max-age=86400"); // 缓存1天
            headers.add("X-Content-Type-Options", "nosniff"); // 安全头
            
            // 🔧 视频支持：添加 Range 支持头，允许浏览器请求部分内容（用于视频拖动）
            if (mimeType != null && mimeType.startsWith("video/")) {
                headers.add("Accept-Ranges", "bytes");
            }
            
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentLength(fileInfo.getFileSize() != null ? fileInfo.getFileSize() : -1)
                    .body(new InputStreamResource(inputStream));
                    
        } catch (Exception e) {
            log.error("媒体预览失败: fileId={}", fileId, e);
            return ResponseEntity.internalServerError().build();
        }
    }
    
    @Operation(summary = "获取预签名下载URL", description = "获取文件的预签名下载URL")
    @GetMapping("/download-url/{fileId}")
    @RequiresPermission("file:fileinfo:download")
    @RateLimit(key = "file:download:url", count = 50, time = 60, limitType = RateLimit.LimitType.USER)
    public Result<String> getDownloadUrl(
            @Parameter(description = "文件ID") @PathVariable String fileId,
            @Parameter(description = "有效期（分钟）") @RequestParam(value = "expireMinutes", defaultValue = "60") int expireMinutes) {
        
        try {
            String url = fileInfoService.getPreSignedDownloadUrl(fileId, expireMinutes);
            log.info("返回下载URL: {}", url);
            return Result.success(url);
        } catch (Exception e) {
            log.error("获取下载URL失败", e);
            return Result.error("获取下载URL失败: " + e.getMessage());
        }
    }
    
    // ==================== 基础CRUD操作 ====================
    
    @Operation(summary = "分页查询文件", description = "分页查询文件信息")
    @GetMapping("/list")
    @RequiresPermission("file:fileinfo:list")
    public Result<PageResponse<FileInfoVO>> listFiles(
            @Parameter(description = "当前页") @RequestParam(value = "current", defaultValue = "1") long current,
            @Parameter(description = "每页大小") @RequestParam(value = "size", defaultValue = "10") long size,
            @Parameter(description = "关键词") @RequestParam(value = "keyword", required = false) String keyword,
            @Parameter(description = "业务类型") @RequestParam(value = "businessType", required = false) String businessType,
            @Parameter(description = "上传者ID") @RequestParam(value = "uploaderId", required = false) String uploaderId,
            @Parameter(description = "状态") @RequestParam(value = "status", required = false) Integer status) {
        
        IPage<FileInfoVO> page = fileInfoService.listFiles(current, size, keyword, businessType, uploaderId, status);
        return Result.success(PageResponse.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
    }
    
    @Operation(summary = "根据ID查询文件", description = "根据ID查询文件详细信息")
    @GetMapping("/{fileId}")
    @RequiresPermission("file:fileinfo:get")
    public Result<FileInfoVO> getFileById(@Parameter(description = "文件ID") @PathVariable String fileId) {
        FileInfoVO vo = fileInfoService.getVOById(fileId);
        if (vo == null) {
            return Result.error("文件不存在");
        }
        return Result.success(vo);
    }
    
    @Operation(summary = "更新文件信息", description = "更新文件信息")
    @PutMapping("/{fileId}")
    @RequiresPermission("file:fileinfo:update")
    @Audit(value = "更新文件信息")
    public Result<FileInfoVO> updateFile(
            @Parameter(description = "文件ID") @PathVariable String fileId,
            @Parameter(description = "文件信息") @RequestBody @Valid FileInfo fileInfo) {
        
        try {
            FileInfo updated = fileInfoService.updateFileInfo(fileId, fileInfo);
            FileInfoVO vo = fileInfoService.getVOById(updated.getId());
            return Result.success(vo);
        } catch (Exception e) {
            log.error("更新文件信息失败", e);
            return Result.error("更新文件信息失败: " + e.getMessage());
        }
    }
    
    @Operation(summary = "删除文件", description = "删除文件（逻辑删除）")
    @DeleteMapping("/{fileId}")
    @RequiresPermission("file:fileinfo:delete")
    @Audit(value = "删除文件")
    public Result<Boolean> deleteFile(@Parameter(description = "文件ID") @PathVariable String fileId) {
        try {
            boolean result = fileInfoService.deleteFile(fileId);
            return Result.success(result);
        } catch (Exception e) {
            log.error("删除文件失败", e);
            return Result.error("删除文件失败: " + e.getMessage());
        }
    }
    
    @Operation(summary = "批量删除文件", description = "批量删除文件")
    @DeleteMapping("/batch")
    @RequiresPermission("file:fileinfo:delete")
    @Audit(value = "批量删除文件")
    public Result<Integer> batchDeleteFiles(
            @Parameter(description = "文件ID列表") @RequestBody List<String> fileIds) {
        
        try {
            int count = fileInfoService.batchDeleteFiles(fileIds);
            return Result.success(count);
        } catch (Exception e) {
            log.error("批量删除文件失败", e);
            return Result.error("批量删除文件失败: " + e.getMessage());
        }
    }
    
    // ==================== 文件查询 ====================
    
    @Operation(summary = "根据业务键查询文件", description = "根据业务键查询文件列表")
    @GetMapping("/business-key/{businessKey}")
    @RequiresPermission("file:fileinfo:list")
    public Result<List<FileInfoVO>> listByBusinessKey(
            @Parameter(description = "业务键") @PathVariable String businessKey) {
        
        List<FileInfo> files = fileInfoService.listByBusinessKey(businessKey);
        List<FileInfoVO> vos = files.stream()
                .map(fileInfo -> fileInfoService.getVOById(fileInfo.getId()))
                .collect(java.util.stream.Collectors.toList());
        return Result.success(vos);
    }
    
    @Operation(summary = "根据业务类型查询文件", description = "根据业务类型分页查询文件")
    @GetMapping("/business-type/{businessType}")
    @RequiresPermission("file:fileinfo:list")
    public Result<PageResponse<FileInfoVO>> listByBusinessType(
            @Parameter(description = "业务类型") @PathVariable String businessType,
            @Parameter(description = "当前页") @RequestParam(value = "current", defaultValue = "1") long current,
            @Parameter(description = "每页大小") @RequestParam(value = "size", defaultValue = "10") long size) {
        
        IPage<FileInfoVO> page = fileInfoService.listByBusinessType(businessType, current, size);
        return Result.success(PageResponse.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
    }
    
    // ==================== 文件统计 ====================
    
    @Operation(summary = "获取文件统计信息", description = "获取文件统计信息")
    @GetMapping("/stats")
    @RequiresPermission("file:fileinfo:stats")
    public Result<Map<String, Object>> getFileStats(
            @Parameter(description = "上传者ID") @RequestParam(value = "uploaderId", required = false) String uploaderId) {
        
        Map<String, Object> stats = fileInfoService.getFileStats(uploaderId);
        return Result.success(stats);
    }
    
    // ==================== 高级功能 ====================
    
    @Operation(summary = "复制文件", description = "复制文件到新的业务场景")
    @PostMapping("/{fileId}/copy")
    @RequiresPermission("file:fileinfo:copy")
    @Audit(value = "复制文件")
    public Result<FileInfoVO> copyFile(
            @Parameter(description = "源文件ID") @PathVariable String fileId,
            @Parameter(description = "新业务类型") @RequestParam("newBusinessType") String newBusinessType,
            @Parameter(description = "新业务键") @RequestParam(value = "newBusinessKey", required = false) String newBusinessKey,
            @Parameter(description = "新描述") @RequestParam(value = "newDescription", required = false) String newDescription) {
        
        try {
            FileInfo newFile = fileInfoService.copyFile(fileId, newBusinessType, newBusinessKey, newDescription);
            FileInfoVO vo = fileInfoService.getVOById(newFile.getId());
            return Result.success(vo);
        } catch (Exception e) {
            log.error("复制文件失败", e);
            return Result.error("复制文件失败: " + e.getMessage());
        }
    }
    
    @Operation(summary = "重命名文件", description = "重命名文件")
    @PutMapping("/{fileId}/rename")
    @RequiresPermission("file:fileinfo:update")
    @Audit(value = "重命名文件")
    public Result<FileInfoVO> renameFile(
            @Parameter(description = "文件ID") @PathVariable String fileId,
            @Parameter(description = "新文件名") @RequestParam("newDisplayName") String newDisplayName) {
        
        try {
            FileInfo updated = fileInfoService.renameFile(fileId, newDisplayName);
            FileInfoVO vo = fileInfoService.getVOById(updated.getId());
            return Result.success(vo);
        } catch (Exception e) {
            log.error("重命名文件失败", e);
            return Result.error("重命名文件失败: " + e.getMessage());
        }
    }
    
    @Operation(summary = "设置文件标签", description = "设置文件标签")
    @PutMapping("/{fileId}/tags")
    @RequiresPermission("file:fileinfo:update")
    @Audit(value = "设置文件标签")
    public Result<FileInfoVO> setFileTags(
            @Parameter(description = "文件ID") @PathVariable String fileId,
            @Parameter(description = "标签") @RequestParam("tags") String tags) {
        
        try {
            FileInfo updated = fileInfoService.setFileTags(fileId, tags);
            FileInfoVO vo = fileInfoService.getVOById(updated.getId());
            return Result.success(vo);
        } catch (Exception e) {
            log.error("设置文件标签失败", e);
            return Result.error("设置文件标签失败: " + e.getMessage());
        }
    }
    
    // ==================== 调试功能 ====================
    
    /**
     * 调试接口：获取公开API扫描结果
     */
    @Operation(summary = "调试：获取公开API扫描结果", description = "用于调试PublicApi注解扫描")
    @GetMapping("/debug/public-apis")
    @PublicApi(description = "调试公开API扫描")
    public Result<Map<String, Object>> getPublicApiDebugInfo() {
        // 这个接口本身就是@PublicApi，用于测试扫描是否正常
        Map<String, Object> debug = new HashMap<>();
        debug.put("message", "此接口标注了@PublicApi，如果能访问说明扫描正常");
        debug.put("timestamp", LocalDateTime.now());
        debug.put("contextPath", "检查日志中的PublicApiSecurityConfigurer扫描结果");
        return Result.success(debug);
    }
    
    // ==================== 管理员功能 ====================
    
    @Operation(summary = "清理过期文件", description = "清理过期文件（管理员功能）")
    @PostMapping("/admin/cleanup-expired")
    @RequiresPermission("file:fileinfo:admin")
    @Audit(value = "清理过期文件")
    public Result<Integer> cleanupExpiredFiles(
            @Parameter(description = "批次大小") @RequestParam(value = "batchSize", defaultValue = "100") int batchSize) {
        
        try {
            int count = fileInfoService.cleanupExpiredFiles(batchSize);
            return Result.success(count);
        } catch (Exception e) {
            log.error("清理过期文件失败", e);
            return Result.error("清理过期文件失败: " + e.getMessage());
        }
    }
    
    @Operation(summary = "清理未使用文件", description = "清理长时间未访问的文件（管理员功能）")
    @PostMapping("/admin/cleanup-unused")
    @RequiresPermission("file:fileinfo:admin")
    @Audit(value = "清理未使用文件")
    public Result<Integer> cleanupUnusedFiles(
            @Parameter(description = "天数") @RequestParam(value = "days", defaultValue = "90") int days,
            @Parameter(description = "批次大小") @RequestParam(value = "batchSize", defaultValue = "100") int batchSize) {
        
        try {
            int count = fileInfoService.cleanupUnusedFiles(days, batchSize);
            return Result.success(count);
        } catch (Exception e) {
            log.error("清理未使用文件失败", e);
            return Result.error("清理未使用文件失败: " + e.getMessage());
        }
    }
}
